package com.kyleromano.disgaea;

import java.io.IOException;

import android.app.Activity;
import android.app.AlertDialog;
import android.app.ProgressDialog;
import android.content.ContentValues;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.res.Resources;
import android.database.Cursor;

import android.graphics.drawable.Drawable;

import android.os.AsyncTask;
import android.os.Bundle;
import android.text.Html;
import android.view.View;
import android.view.View.OnFocusChangeListener;

import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.CompoundButton;
import android.widget.CompoundButton.OnCheckedChangeListener;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.Spinner;
import android.widget.TextView;
import android.widget.ToggleButton;
import android.widget.Toast;
import android.widget.NumberPicker;
import android.widget.Button;

import com.kyleromano.disgaea.dataType.Classes;
import com.kyleromano.disgaea.dataType.Job;
import com.kyleromano.disgaea.dataType.ParentJob;
import com.kyleromano.disgaea.dataType.Tier;
import com.kyleromano.disgaea.sql.CharacterDataSource;
import com.kyleromano.disgaea.sql.DBConstants;

/**
 * Character viewing and editing activity
 * Display lists of created characters and drill down to get more details about specific characters
 * including levels, name, classes etc.
 * @author kromano
 *
 */
public class CharacterViewActivity extends Activity {
	private Spinner jobs, tiers;
	private Character character;
	private ToggleButton editMode;
	private NumberPicker thousands, hundreds, tens, ones;
	private int charID = 0;
	private CharacterDataSource db;
	private boolean dataChanged = false;
	private boolean newCharacter = false;
	private boolean dataSaved = false;
	public static final int RESULT_CHAR_DELETED = 2;
	public static final int RESULT_DATA_CHANGED = 3;
	public static final int RESULT_NEW_CHARACTER = 4;
	public static final int RESULT_CHARACTER_NOT_FOUND = 5;
	
	/**
	 * On initial creation only
	 */
	@Override
    public void onCreate(Bundle savedInstanceState)
	{
		super.onCreate(savedInstanceState);
		this.setContentView(R.layout.character_view_edit);
		db = new CharacterDataSource(this);
		db.open();
        //Parent job selector
        jobs = (Spinner)this.findViewById(R.id.parentJob);
        ArrayAdapter<Job> jobAdapter = new ArrayAdapter<Job>(this, android.R.layout.simple_spinner_item, buildJobArray());
        jobs.setAdapter(jobAdapter);
        jobAdapter.setDropDownViewResource(android.R.layout.simple_dropdown_item_1line);
        
        tiers = (Spinner)this.findViewById(R.id.selectJob);
        
        //Top level job selection
        tiers.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
        	public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) {
        		if(character != null)
        		{
        			updateTier(((Tier)tiers.getSelectedItem()).getID());
        			try
        			{
        				character.updatePortrait(parent.getResources().getAssets());
        			} catch(IOException e) {
        				e.printStackTrace();
        			}
        		}
        		drawPortrait(tiers.getSelectedItemPosition());
        		
        	}
        	public void onNothingSelected(AdapterView<?> parent){}
        });
        
        //Job class rank/tier selection
       jobs.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
        	//Update the Tier list based on the selected Job/Race
        	public void onItemSelected(AdapterView<?> parent, View view, int pos, long id)
        	{
        		//Obtain me some objects
        		Job selectedJob = (Job) parent.getSelectedItem();
        		Tier[] tierArray = selectedJob.getTiers();
        		ArrayAdapter<Tier> tierAdapter = 
        				new ArrayAdapter<Tier>(parent.getContext(), android.R.layout.simple_spinner_item, tierArray);
        		tiers.setAdapter(tierAdapter);
        		tierAdapter.setDropDownViewResource(android.R.layout.simple_dropdown_item_1line);
        		if(character != null)
        		{
        			int charClass = character.getCharClass();
        			int classPosition = 0;
        			boolean found = false;
        			while(!found && classPosition < tiers.getCount())
        			{
        				if(tierArray[classPosition].getID() == charClass)
        					found = true;
        				else
        					classPosition++;
        			}
        			if(classPosition > 0 && !found)
        				classPosition = 0;
        			tiers.setSelection(classPosition);
        		}
        		if(character != null) {
        			updateJob(((Job)jobs.getSelectedItem()).getJobID());
        			refreshProficiencyString();
        		}
        	}
        	public void onNothingSelected(AdapterView<?> parent) { /*Do nothing*/ }
        });
       
       editMode = (ToggleButton)this.findViewById(R.id.buttonToggleEditMode);
       editMode.setOnCheckedChangeListener(new OnCheckedChangeListener(){

		public void onCheckedChanged(CompoundButton buttonView,
				boolean isChecked) {
			refreshControls(isChecked);	//get the button state and pass it to the controls
			}
       });
      
   		thousands = (android.widget.NumberPicker)this.findViewById(R.id.thousands);
   		hundreds = (android.widget.NumberPicker)this.findViewById(R.id.hundreds);
   		tens = (android.widget.NumberPicker)this.findViewById(R.id.tens);
   		ones = (android.widget.NumberPicker)this.findViewById(R.id.ones);
   		
   		thousands.setDescendantFocusability(NumberPicker.FOCUS_BLOCK_DESCENDANTS);
   		hundreds.setDescendantFocusability(NumberPicker.FOCUS_BLOCK_DESCENDANTS);
   		tens.setDescendantFocusability(NumberPicker.FOCUS_BLOCK_DESCENDANTS);
   		ones.setDescendantFocusability(NumberPicker.FOCUS_BLOCK_DESCENDANTS);
       
   		charID = getIntent().getExtras().getInt("id");
   		if(charID == -1)
   			newCharacter();
   		else
   			loadCharacter();
       setLevelNumberPicker();
       refreshControls(editMode.isChecked());

       //Toast.makeText(this, jobs.getChildCount() + "", Toast.LENGTH_SHORT).show();
       //tiers.setSelection(2);
       //SpinnerAdapter sa = tiers.getAdapter();
       //Toast.makeText(this, sa.getItem(0) + " ", Toast.LENGTH_SHORT).show();

       Button back = (Button)this.findViewById(R.id.backButton);
       back.setOnClickListener(new View.OnClickListener() {
    	   public void onClick(View view) {
    		   Intent intent = new Intent();
    		   if(newCharacter && dataSaved) {
    			   setResult(RESULT_NEW_CHARACTER, intent);
    			   //int changeId, int newLevel, String newName, int newJobId, int newClassId
    			   packageIntent(intent);
    		   } else if(dataChanged && dataSaved) {
    			   setResult(RESULT_DATA_CHANGED, intent);
    			   //int changeId, int newLevel, String newName, int newJobId, int newClassId
    			   packageIntent(intent);
    		   } else
    			   setResult(RESULT_OK, intent);
    		   finish();
    	   }
       });
	}
       /**
        * Run common packaging to get latest data from the character
        * @param intent
        */
       private void packageIntent(Intent intent) {
    	   EditText e = (EditText)findViewById(R.id.editCharName);
	    	Job j = (Job)jobs.getSelectedItem();
	    	Tier c = (Tier)tiers.getSelectedItem();

	    	String name = e.getText().toString().trim();
	    	int level = (thousands.getValue() * 1000) + (hundreds.getValue() * 100) + (tens.getValue() * 10) + ones.getValue();
	    	int cClass = c.getID();
	    	String className = c.toString();
	    	int job = j.getID();
	    	
		   intent.putExtra("changeId", charID);
		   intent.putExtra("newLevel", level);
		   intent.putExtra("newJobId", job);
		   intent.putExtra("newClassId", cClass);
		   intent.putExtra("newName", name);
		   intent.putExtra("newClassString", className);
       }
	
	@Override
	public void onDestroy()
	{
		super.onDestroy();
		db.close();
	}
	
	@Override
	public void onPause()
	{
		super.onPause();
		db.close();
	}
	
	@Override
	public void onResume()
	{
		super.onResume();
		db.open();
	}
	
	/**
	 * Sets the text based on character selection for Weapon Proficiencies or Magichange type
	 */
	private void refreshProficiencyString()
	{
		TextView t = (TextView)findViewById(R.id.textViewWeapon);
		t.setText(Html.fromHtml(character.getProficiencies()));
	}

    /**
     * Load the character data and populate the GUI
     */
	private void loadCharacter()
	{
		try
		{
			Cursor c = db.getOneCharacter(charID);
			if(c != null && c.getCount() > 0)
			{
				c.moveToFirst();
				String n = c.getString(1);
				int l = c.getInt(2);
				int cID = c.getInt(3);
				int jID = c.getInt(5);
				character = new Character(n, cID, jID, l, this);
				TextView name = (TextView) this.findViewById(R.id.charName);
				EditText nameEdit = (EditText)this.findViewById(R.id.editCharName);
				name.setText(character.getName());
				nameEdit.setText(character.getName());
				jobs.setSelection(getSelectionID(character.getJobParent()));
				c.close();
			} else {
				Intent intent = new Intent();
 		    	setResult(RESULT_CHARACTER_NOT_FOUND, intent);
 		    	finish();
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	/**
	 * Build a new Character from default values
	 */
	private void newCharacter()
	{
		try {
			character = new Character("", Classes.FIGHTER, ParentJob.WARRIOR, 1, this);
			//TextView name = (TextView) this.findViewById(R.id.charName);
			//EditText nameEdit = (EditText)this.findViewById(R.id.editCharName);
			//name.setText(character.getName());
			//nameEdit.setText(character.getName());
			jobs.setSelection(getSelectionID(character.getJobParent()));
			editMode.toggle();
			newCharacter = true;
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

    /**
     * Convert parent job ID to a row selection
     * @param cID Character's parent job ID
     * @return index value between 0 and 38, inclusive
     */
    @SuppressWarnings("unchecked")
	private int getSelectionID(int cID) {
    	ArrayAdapter<Job> jobAdapter = (ArrayAdapter<Job>)jobs.getAdapter();
    	boolean found = false;
    	int i = 0;
    	while(!found && i < jobAdapter.getCount())
    	{
    		if(cID == jobAdapter.getItem(i).getJobID())
    			found = true;
			else
				i++;
		}
		if(i > 0 && !found)
			i = 0;
		return i;
	}

	/**
     * Draw the portrait
     */
    private void drawPortrait(int index)
    {
    	ImageView portrait = (ImageView)this.findViewById(R.id.charPortrait);
        if(character.getPortrait(index) != null)
        	portrait.setImageBitmap(character.getPortrait(index));
        else {
            try {
            	portrait.setImageDrawable(Drawable.createFromStream(this.getAssets().open("portraits/drfunke.png"),null));
            } catch (IOException e) {
            	System.err.println("Now you've done it... Here's the stack trace:");
            	e.printStackTrace();
            }
        }
    }
    
    /**
     * Update character data
     */
    private void updateTier(int newTierID) { character.setCharClass(newTierID); }
    private void updateJob(int newJobID) { character.setJobParent(newJobID,this); }
    
    /**
     * Build me some doods
     */
    private Job[] buildJobArray()
    {
    	Resources r = this.getResources();
        Job[] jobArray = new Job[39];
        jobArray[0]  = new Job(ParentJob.WARRIOR, r);
        jobArray[1]  = new Job(ParentJob.VALKYRIE, r);
        jobArray[2]  = new Job(ParentJob.WITCH, r);
        jobArray[3]  = new Job(ParentJob.MAGICIAN, r);
        jobArray[4]  = new Job(ParentJob.HEALER, r);
        jobArray[5]  = new Job(ParentJob.ARCHER, r);
        jobArray[6]  = new Job(ParentJob.GUNNER, r);
        jobArray[7]  = new Job(ParentJob.ARMORKNIGHT, r);
        jobArray[8]  = new Job(ParentJob.THIEF, r);
        jobArray[9]  = new Job(ParentJob.NINJA, r);
        jobArray[10] = new Job(ParentJob.SAMURAI, r);
        jobArray[11] = new Job(ParentJob.BEASTMASTER, r);
        jobArray[12] = new Job(ParentJob.SORCERER, r);
        jobArray[13] = new Job(ParentJob.BOUNCER, r);
        jobArray[14] = new Job(ParentJob.PROFESSOR, r);
        jobArray[15] = new Job(ParentJob.MAGICKNIGHT, r);
        jobArray[16] = new Job(ParentJob.MASKEDHERO, r);
        jobArray[17] = new Job(ParentJob.ONMYOMONK, r);
        jobArray[18] = new Job(ParentJob.BATTLESUIT, r);
        jobArray[19] = new Job(ParentJob.ANDROID, r);
        jobArray[20] = new Job(ParentJob.SLUDGE, r);
        jobArray[21] = new Job(ParentJob.ORC, r);
        jobArray[22] = new Job(ParentJob.MOTHMAN, r);
        jobArray[23] = new Job(ParentJob.SHROOM, r);
        jobArray[24] = new Job(ParentJob.GHOST, r);
        jobArray[25] = new Job(ParentJob.SLUMBERCAT, r);
        jobArray[26] = new Job(ParentJob.PRINNY, r);
        jobArray[27] = new Job(ParentJob.ZOMBIE, r);
        jobArray[28] = new Job(ParentJob.GARGOYLE, r);
        jobArray[29] = new Job(ParentJob.FLORABEAST, r);
        jobArray[30] = new Job(ParentJob.ROC, r);
        jobArray[31] = new Job(ParentJob.REAPER, r);
        jobArray[32] = new Job(ParentJob.MYSTICBEAST, r);
        jobArray[33] = new Job(ParentJob.SUCCUBUS, r);
        jobArray[34] = new Job(ParentJob.DRAGONZOMBIE, r);
        jobArray[35] = new Job(ParentJob.WOODGOLEM, r);
        jobArray[36] = new Job(ParentJob.DRAGON, r);
        jobArray[37] = new Job(ParentJob.FELYNN, r);
        jobArray[38] = new Job(ParentJob.RIFLEDEMON, r);
        
        return jobArray;
    }
    
    /**
     * Create and set values for Thousands, Hundreds, Tens, and Ones number pickers
     */
    private void setLevelNumberPicker()
    {
    	NumberPicker[] picker = new NumberPicker[4];
    	picker[3] = (thousands);
    	picker[2] = (hundreds);
    	picker[1] = (tens);
    	picker[0] = (ones);

    	for(int i = 0; i < 4; i++)
    	{
    		picker[i].setMinValue(0);
    		picker[i].setMaxValue(9);
    	}
    	
    	String level = character.getLevel() + "";
    	int digits = level.length();
    	if(digits == 1)
    		picker[0].setValue(Integer.parseInt(level.charAt(0)+""));
    	else if(digits == 2) {
    		picker[0].setValue(Integer.parseInt(level.charAt(1)+""));
    		picker[1].setValue(Integer.parseInt(level.charAt(0)+""));
    	}
    	else if(digits == 3){
    		picker[0].setValue(Integer.parseInt(level.charAt(2)+""));
    		picker[1].setValue(Integer.parseInt(level.charAt(1)+""));
    		picker[2].setValue(Integer.parseInt(level.charAt(0)+""));
    	}
    	else if(digits == 4){
    		picker[0].setValue(Integer.parseInt(level.charAt(3)+""));
    		picker[1].setValue(Integer.parseInt(level.charAt(2)+""));
    		picker[2].setValue(Integer.parseInt(level.charAt(1)+""));
    		picker[3].setValue(Integer.parseInt(level.charAt(0)+""));
    	}

    	//Toast.makeText(this, level[0] + " " + level[1], Toast.LENGTH_SHORT).show();
    }
    
    /**
     * Updates controls to enable for editing or lock them down to avoid changes
     * @param isEnabled The state of the toggle button
     */
    private void refreshControls(boolean isEnabled)
    {
    	TextView levelText = (TextView)this.findViewById(R.id.levelDisplay);
    	TextView nameView = (TextView)this.findViewById(R.id.charName);
    	EditText nameEdit = (EditText)this.findViewById(R.id.editCharName);
    	Button backButton = (Button)this.findViewById(R.id.backButton);
    	Button saveChanges = (Button)this.findViewById(R.id.characterEditSaveButton);
    	Button delete = (Button)this.findViewById(R.id.characterEditViewDelete);
    	if(isEnabled){	//We are in edit mode, hide the static object and reveal the pickers
    		//character level
    		levelText.setVisibility(TextView.GONE);
    		thousands.setVisibility(NumberPicker.VISIBLE);
       		hundreds.setVisibility(NumberPicker.VISIBLE);
       		tens.setVisibility(NumberPicker.VISIBLE);
       		ones.setVisibility(NumberPicker.VISIBLE);
       		//Character name
       		nameView.setVisibility(TextView.GONE);
    		nameEdit.setVisibility(EditText.VISIBLE);
    		saveChanges.setEnabled(true);
    		backButton.setVisibility(TextView.GONE);
    		delete.setVisibility(TextView.VISIBLE);
    		if(charID == -1)
    			delete.setEnabled(false);
    		else
    			delete.setEnabled(true);
       		
    	} else {	//We are in reading mode, hide the pickers and reveal the static object
    		//character level
    		thousands.setVisibility(NumberPicker.GONE);
       		hundreds.setVisibility(NumberPicker.GONE);
       		tens.setVisibility(NumberPicker.GONE);
       		ones.setVisibility(NumberPicker.GONE);
    		String pickerValue = thousands.getValue()	+ "" +
    		                     hundreds.getValue()	+ "" + 
    				             tens.getValue()		+ "" +
    		                     ones.getValue();
    		levelText.setText(pickerValue);
    		levelText.setVisibility(TextView.VISIBLE);
    		//character name
    		nameView.setText(nameEdit.getText());
    		nameEdit.setVisibility(EditText.GONE);
    		nameView.setVisibility(TextView.VISIBLE);
    		saveChanges.setEnabled(false);
    		backButton.setVisibility(TextView.VISIBLE);
    		delete.setVisibility(TextView.GONE);
    	}
    	
    	jobs.setEnabled(isEnabled);
    	tiers.setEnabled(isEnabled);
    }
    
    /**
     * Write changes back to the Database
     */
    public void saveChanges(View view)
    {
    	boolean successful = false;
    	EditText e = (EditText)this.findViewById(R.id.editCharName);
    	Job j = (Job)jobs.getSelectedItem();
    	Tier c = (Tier)tiers.getSelectedItem();

    	String name = e.getText().toString().trim();
    	int level = (thousands.getValue() * 1000) + (hundreds.getValue() * 100) + (tens.getValue() * 10) + ones.getValue();
    	int cClass = c.getID();
    	int job = j.getID();

    	ContentValues cv = new ContentValues();
    	// Check to make sure the character has a name entry and level > 0
    	if(!(name.trim().length() == 0) && level > 0) {
    		character.setName(name);
			character.setLevel(level);
			character.setCharClass(cClass);
			character.setJobParent(job, this);
    		// Populate the common values ahead of time
    		cv.put(DBConstants.CCHARNAME, name);
    		cv.put(DBConstants.CLEVEL, level);
    		cv.put(DBConstants.CCLASSID, cClass);
    		cv.put(DBConstants.CJOBID, job);

    		// Test if this is a new character or an existing record
    		if(charID == -1)
    		{
    			// This is a new character, create a new record
    			int id = (db.getLastID() +1);
    			cv.put(DBConstants.CCID, id);
    			if(db.insert(DBConstants.TCHARACTERS, null, cv)) {
    				Toast.makeText(this, getResources().getString(R.string.sNewCharacterSaved), Toast.LENGTH_SHORT).show();
    				successful = true;
    			}
    			// Update the char id in case the user returns to make changes before exiting back to the Character List
    			charID = id;
    		} else {
    			// This was an old character, update an existing record
    			if(db.update(DBConstants.TCHARACTERS, cv, DBConstants.CCID+"="+charID, null)) {
    				Toast.makeText(this, String.format(getResources().getString(R.string.sUpdated, name)), Toast.LENGTH_SHORT).show();
    				successful = true;
    			}
    		}
    		// Toggle out of Edit Mode if the record was saved
    		if(successful) {
    			editMode.toggle();
    			dataChanged = true;
    			dataSaved = true;
				//checkBasicRequirements(level, cClass, job);
				Integer[] params = new Integer[] { level, cClass, job };
				CheckRequirementsThread thread = new CheckRequirementsThread(this);
				thread.execute(params);
    		}
    		// Errored out because Level or Name was invalid
    	} else {
    		if(level == 0)
    			Toast.makeText(this, getResources().getString(R.string.sErrorLevelGreaterThanZero), Toast.LENGTH_LONG).show();
    		else
    			Toast.makeText(this, getResources().getString(R.string.sErrorCharacterMustHaveName), Toast.LENGTH_LONG).show();
    	}
    }
    
    /**
     * Delete the selected character from the database
     * Prompts for confirmation of action
     * @param view
     */
    public void deleteRecord(View view)
    {
    	String message = getResources().getString(R.string.sConfirmDelete);
    	AlertDialog.Builder builder = new AlertDialog.Builder(this);
    	builder.setMessage(String.format(message, character.getName()));
    	builder.setCancelable(false);
    	builder.setPositiveButton(getResources().getString(R.string.sYes), new DialogInterface.OnClickListener() {
    	           public void onClick(DialogInterface dialog, int id) {
    	                if(db.deleteRecord(DBConstants.TCHARACTERS, DBConstants.CCID, charID + "")) {
    	                	Intent intent = new Intent();
    	                	intent.putExtra("deletedCharacterId", charID);
    	                	intent.putExtra("deletedCharacterName", character.getName());
    	     		    	setResult(RESULT_CHAR_DELETED, intent);
    	     		    	finish();
    	                }
    	           }
    	       });
    	builder.setNegativeButton(getResources().getString(R.string.sNo), new DialogInterface.OnClickListener() {
    	           public void onClick(DialogInterface dialog, int id) {
    	                dialog.cancel();
    	           }
    	       });
    	builder.create().show();
    }
    
	/**
     * Threaded query for handling the construction of the player data list
     * @author kromano
     * Params, the type of the parameters sent to the task upon execution. 
	 * Progress, the type of the progress units published during the background computation. 
	 * Result, the type of the result of the background computation. 
     *
     */
    private class CheckRequirementsThread extends AsyncTask<Integer, Void, Void> {
    	private ProgressDialog loadingBar;
    	private Context context;
    	
    	private CheckRequirementsThread(Context context) {
    		this.context = context;
    	}
    	
    	@Override
    	protected Void doInBackground(Integer... data) {
    		int level	= data[0];
    		int classID	= data[1];
    		int jobID	= data[2];
    		checkBasicRequirements(level, classID, jobID);
			return null;
    	}
    	
    	@Override
    	protected void onPreExecute() {
    		super.onPreExecute();
    		// Show a Spinner progressDialog
    		loadingBar = new ProgressDialog(context, ProgressDialog.STYLE_SPINNER);
    		loadingBar.setTitle(getResources().getString(R.string.sSaving));
    		loadingBar.show();
    	}
    	
    	@Override
    	protected void onProgressUpdate(Void... v) {
    		super.onProgressUpdate(v);
    	}

    	@Override
    	protected void onPostExecute(Void v) { 
    		super.onPostExecute(v);
    		loadingBar.dismiss();
    	}
        /***********************************************************************************
         *                                                                                 *
         * Check unlock requirements at save time rather than when opening the Unlock view *
         * in case you create a class that meets a requirement then edit that class before *
         * viewing it in Unlock.                                                           *
         *                                                                                 *
         ***********************************************************************************/
        /**
         * Perform the SQL operation to unlock the classID
         * @param classID The individual classID to unlock
         */
        private void unlockClass(int classID) {
        	ContentValues cv = new ContentValues();
        	cv.put(DBConstants.CCLASSUNLOCKED, 1);
        	db.update(DBConstants.TCLASSES, cv, DBConstants.CCLASSID + "=" + classID, null);
        }
        
        /**
    	 * Read saved character data to determine which classes should be unlocked automatically
    	 * This should probably be run in its own thread due to the number of SQL statements it performs
    	 * @param level The level of the current character we're checking
    	 * @param classID The Class ID of the class we're checking
    	 * @param jobID The Job ID of the class we're checking
    	 */
        private void checkBasicRequirements(int level, int classID, int jobID) {
        	if(jobID == ParentJob.WARRIOR)
        		checkAdvancedRequirements(Classes.HEAVYKNIGHT);
        	else if (jobID == ParentJob.WARRIOR		|| 
        			jobID == ParentJob.MAGICIAN	||
        			jobID == ParentJob.VALKYRIE	||
        			jobID == ParentJob.WITCH)
        		checkAdvancedRequirements(Classes.MAGICKNIGHT);
        	int levelRequirement = Classes.getUpgradeRequirement(classID);
        	//If the level requirement is returned less than 0 it is a special case, otherwise do a basic level comparison check
        	if(levelRequirement > 0) {
        		if(level >= levelRequirement) {
        			if(jobID == classID) {
        				//we don't actually need to do anything because this is the first level
        				unlockClass(classID + 1);
        			} else if ( !(classID == (jobID + 5)) ) {
        				String select = "SELECT " + DBConstants.CCLASSID + ", " + DBConstants.CCLASSUNLOCKED + " FROM " + DBConstants.TCLASSES +
        						" WHERE " + DBConstants.CCLASSUNLOCKED + "=1 AND " + DBConstants.CCLASSID + "=" + (classID -1) + ";";
        				Cursor c = db.select(select, null);
        				//Make sure the previous tier has been unlocked to avoid getting stuck
        				if(c != null && c.getCount() > 0) {
        					//It's safe to unlock the next tier
        					unlockClass(classID + 1);
        					c.close();
        				}
        			}
        		}
        		//-1 defines no next tier to unlock
        	} else if(levelRequirement == -2) {
        		//It's a mage!
        		int mage1 = -1;
        		int mage2 = -1;
        		int mage3 = -1;
        		boolean skull = false;
        		String[] mage = new String[] { Classes.REDMAGE + "", Classes.GREENMAGE + "", Classes.BLUEMAGE + "",
        				Classes.REDSKULL + "", Classes.GREENSKULL + "", Classes.BLUESKULL + ""};
        		String where = "";
        		for(int i = 0; i < 5; i++) {
        			where += DBConstants.CCLASSID + "=" + mage[i] + " OR ";
        		}
        		where += DBConstants.CCLASSID + "=" + mage[5] + " GROUP BY " + DBConstants.CCLASSID + ";";
        		String select = "SELECT " + DBConstants.CCLASSID + ", MAX(" + DBConstants.CLEVEL + ") as level FROM " +
        				DBConstants.VCHARACTER + " WHERE " + where;
        		Cursor c = db.select(select, null);
        		//get the levels of the three required classes, either Skull or Mage
        		if(classID == Classes.REDMAGE || classID == Classes.GREENMAGE || classID == Classes.BLUEMAGE) {
        			mage1 = getClassLevelFromCursor(Classes.REDMAGE, c);
        			mage2 = getClassLevelFromCursor(Classes.GREENMAGE, c);
        			mage3 = getClassLevelFromCursor(Classes.BLUEMAGE, c);
        		} else if(classID == Classes.REDSKULL || classID == Classes.GREENSKULL || classID == Classes.BLUESKULL) {
        			mage1 = getClassLevelFromCursor(Classes.REDSKULL, c);
        			mage2 = getClassLevelFromCursor(Classes.GREENSKULL, c);
        			mage3 = getClassLevelFromCursor(Classes.BLUESKULL, c);
        			skull = true;
        		}
        		//Check to make sure that all three values have been initialized otherwise requirements aren't met
        		if(mage1 > 0 && mage2 > 0 && mage3 > 0)
        			//Check the total of all three is greater than or equal to 50 and unlock the next tier if true
        			if( (mage1 + mage2 + mage3) >= 50 ) {
        				if(skull)
        					unlockClass(Classes.STARSKULL);
        				else
        					unlockClass(Classes.STARMAGE);
        			}
        	}
        	// Check multi-tier requirements based on whether the current class is part of the set
        	if (jobID == ParentJob.HEALER	|| 
        			jobID == ParentJob.VALKYRIE	||
        			jobID == Classes.ARCHER)
        		checkAdvancedRequirements(ParentJob.HEALER, ParentJob.VALKYRIE, Classes.ARCHER);
        	if (jobID == ParentJob.THIEF	|| 
        			jobID == ParentJob.MAGICIAN	||
        			jobID == Classes.GUNNER)
        		checkAdvancedRequirements(ParentJob.THIEF, ParentJob.MAGICIAN, Classes.GUNNER);
        	if (jobID == ParentJob.THIEF	|| 
        			jobID == ParentJob.MAGICIAN	||
        			jobID == Classes.GENIN)
        		checkAdvancedRequirements(ParentJob.THIEF, ParentJob.MAGICIAN, Classes.GENIN);
        	if (jobID == ParentJob.VALKYRIE	|| 
        			jobID == ParentJob.ARCHER	||
        			jobID == Classes.EXORCIST)
        		checkAdvancedRequirements(ParentJob.VALKYRIE, ParentJob.ARCHER, Classes.EXORCIST);
        	if (jobID == ParentJob.WARRIOR	|| 
        			jobID == ParentJob.VALKYRIE	||
        			jobID == Classes.BEASTTAMER)
        		checkAdvancedRequirements(ParentJob.WARRIOR, ParentJob.VALKYRIE, Classes.BEASTTAMER);
        	if (jobID == ParentJob.MAGICIAN	|| 
        			jobID == ParentJob.WITCH	||
        			jobID == Classes.SHAMAN)
        		checkAdvancedRequirements(ParentJob.MAGICIAN, ParentJob.WITCH, Classes.SHAMAN);
        	if (jobID == ParentJob.HEALER		|| 
        			jobID == ParentJob.ONMYOMONK	||
        			jobID == Classes.PROFESSOR)	
        		checkAdvancedRequirements(ParentJob.HEALER, ParentJob.ONMYOMONK, Classes.PROFESSOR);
        	if (jobID == ParentJob.THIEF	|| 
        			jobID == ParentJob.WARRIOR	||
        			jobID == Classes.MASKEDHERO)
        		checkAdvancedRequirements(ParentJob.THIEF, ParentJob.WARRIOR, Classes.MASKEDHERO);
        	if (jobID == ParentJob.SORCERER	|| 
        			jobID == ParentJob.NINJA	||
        			jobID == Classes.ONMYOTRAINEE)
        		checkAdvancedRequirements(ParentJob.SORCERER, ParentJob.NINJA, Classes.ONMYOTRAINEE);
        	if (jobID == ParentJob.WARRIOR		|| 
        			jobID == ParentJob.ARMORKNIGHT	||
        			jobID == Classes.BIOSUIT)
        		checkAdvancedRequirements(ParentJob.WARRIOR, ParentJob.ARMORKNIGHT, Classes.BIOSUIT);
        }
    	
    	/**
    	 * Check requirements
    	 * @param unlockClassID class ID to check
    	 */
    	private void checkAdvancedRequirements(int unlockClassID) {
    		int class1Level = -1;
    		int class2Level = -1;
    		String select = null;
    		Cursor c = null;
    		//Armor Knight
    		if(unlockClassID == Classes.HEAVYKNIGHT) {
    			select = "SELECT " + DBConstants.CJOBID + ", MAX(" + DBConstants.CLEVEL + ") as level FROM " + DBConstants.VCHARACTER +
    					" WHERE " + DBConstants.CJOBID + "=" + ParentJob.WARRIOR +
    					" GROUP BY " + DBConstants.CJOBID + ";";
    			c = db.select(select, null);			
    			if(c != null && c.getCount() > 0)
    				if(c.moveToNext())
    					class1Level = c.getInt(0);
    			if(class1Level >= 20)
    				unlockClass(unlockClassID);
    		//Magic Knight
    		} else if(unlockClassID == Classes.MAGICKNIGHT)	{
    			select = "SELECT " + DBConstants.CJOBID + ", MAX(" + DBConstants.CLEVEL + ") as level FROM " + DBConstants.VCHARACTER +
    					" WHERE " + DBConstants.CJOBID + "=" + ParentJob.WARRIOR +
    					" OR " + DBConstants.CJOBID + "=" + ParentJob.MAGICIAN +
    					" GROUP BY " + DBConstants.CJOBID + ";";
    			c = db.select(select,  null);
    			if(c != null && c.getCount() > 1) {
    				if(c.moveToNext())
    					class1Level = c.getInt(0);
    				if(c.moveToNext())
    					class2Level = c.getInt(0);
    				if(class1Level >= 30 && class2Level >= 30)
    					unlockClass(unlockClassID);
    			} else {
    				select = "SELECT " + DBConstants.CJOBID + ", MAX(" + DBConstants.CLEVEL + ") as level FROM " + DBConstants.VCHARACTER +
    						" WHERE " + DBConstants.CJOBID + "=" + ParentJob.VALKYRIE +
    						" OR " + DBConstants.CJOBID + "=" + ParentJob.WITCH +
    						" GROUP BY " + DBConstants.CJOBID + ";";
    				c = db.select(select,  null);
    				if(c != null && c.getCount() > 1) {
    					if(c.moveToNext())
    						class1Level = c.getInt(0);
    					if(c.moveToNext())
    						class2Level = c.getInt(0);
    					if(class1Level >= 30 && class2Level >= 30)
    						unlockClass(unlockClassID);
    				}
    			}
    		}
    	}
    	
    	/**
    	 * Check two tier requirements
    	 * @param jobID1 First job tier involved in the requirements
    	 * @param jobID2 Second job tier involved in the requirements
    	 * @param unlockClassID The class we're attempting to unlock
    	 */
    	private void checkAdvancedRequirements(int jobID1, int jobID2, int unlockClassID) {
    		String select = null;
    		Cursor c = null;
    		int class1Level = -1;
    		int class2Level = -1;
    		select = "SELECT " + DBConstants.CJOBID + ", MAX(" + DBConstants.CLEVEL + ") as level FROM " + DBConstants.VCHARACTER +
    				" WHERE " + DBConstants.CJOBID + "=" + jobID1 +
    				" OR " + DBConstants.CJOBID + "=" + jobID2 +
    				" GROUP BY " + DBConstants.CJOBID + ";";
    		c = db.select(select, null);			
    		if(c != null && c.getCount() > 1) {
    			if(c.moveToNext())
    				class1Level = c.getInt(0);
    			if(c.moveToNext())
    				class2Level = c.getInt(0);
    			c.close();
    		}
    		switch (unlockClassID) {
    		case Classes.ARCHER :
    		case Classes.GUNNER :
    		case Classes.BEASTMASTER :
    		case Classes.SHAMAN :
    			if(class1Level >= 15 && class2Level >= 15)
    				unlockClass(unlockClassID);
    			break;

    		case Classes.PROFESSOR :
    			if(class1Level >= 20 && class2Level >= 20)
    				unlockClass(unlockClassID);
    			break;

    		case Classes.MASKEDHERO :
    			if(class1Level >= 25 && class2Level >= 25)
    				unlockClass(unlockClassID);
    			break;

    		case Classes.GENIN :
    		case Classes.ONMYOTRAINEE :
    		case Classes.BIOSUIT :
    		case Classes.EXORCIST :
    			if(class1Level >= 30 && class2Level >= 30)
    				unlockClass(unlockClassID);
    			break;
    		}
    	}

    	private int getClassLevelFromCursor(int classID, Cursor c) {
    		int currentPosition = c.getPosition();
    		//c.moveToFirst();
    		c.moveToPosition(-1);
    		boolean found = false;
    		int level = -1;
    		while(c.moveToNext() && !found)
    		{
    			if(c.getInt(0) == classID) {
    				level = c.getInt(1);
    				found = true;
    			}
    		}

    		c.moveToPosition(currentPosition);
    		return level;
    	}
    }
}